package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {


    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;
    @Autowired
    private UserAccountFeignClient userAccountFeignClient;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private OrderDerateMapper orderDerateMapper;
    @Autowired
    private RabbitService rabbitService;

    /**
     * 根据用户ID和订单状态分页查询订单信息
     *
     * 此方法通过调用订单信息映射器（orderInfoMapper）的selectPayList方法，
     * 根据提供的分页对象（page）、订单状态（orderStatus）和用户ID（userId）来获取订单信息的分页数据
     * 然后，对查询结果中的每个订单信息对象进行处理，设置支付方式名称（payWayName）
     * 和订单状态名称（orderStatusName）以提供更易读的数据结构
     *
     * @param page 分页对象，包含了分页查询的页码、每页数量等信息
     * @param orderStatus 订单状态，用于筛选查询结果中订单的信息
     * @param userId 用户ID，用于识别和筛选属于特定用户的订单
     * @return 返回一个分页的订单信息对象（IPage<OrderInfo>），包含了经过处理的订单列表和分页信息
     */
    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> page, String orderStatus, Long userId) {
        IPage<OrderInfo> iPage = orderInfoMapper.selectPayList(page, orderStatus, userId);
        for (OrderInfo orderInfo : iPage.getRecords()) {
            orderInfo.setPayWayName(orderInfo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT) ? "账号" : "微信");
            orderInfo.setOrderStatusName(this.getOrderStatusNameByOrderStatus(orderInfo.getOrderStatus()));

        }
        return iPage;
    }

    /**
     * 根据订单状态获取订单状态的名称
     * 此方法避免了多次if-else语句，通过一次性判断返回对应的订单状态名称
     *
     * @param orderStatus 订单状态，来源于SystemConstant类中的订单状态常量
     * @return 返回对应的订单状态名称如果订单状态不是已支付或未支付，则默认返回"已取消"
     */
    private String getOrderStatusNameByOrderStatus(String orderStatus) {
        return orderStatus.equals(SystemConstant.ORDER_STATUS_UNPAID) ? "未支付" : orderStatus.equals(SystemConstant.ORDER_STATUS_PAID) ? "已支付" : "已取消";
    }

    /**
     * 提交订单方法
     *
     * @param orderInfoVo 订单信息封装对象，包含订单相关数据
     * @param userId      用户ID，用于识别用户身份
     * @return 返回一个Map对象，包含操作结果信息
     * <p>
     * 方法功能：该方法用于处理订单提交操作，包括订单信息校验、流水号校验、生成订单号等步骤
     * 主要目的是确保订单信息的正确性，并防止重复提交订单
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //校验签名
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        map.put("payWay", "");
        SignHelper.checkSign(map);
        //校验流水号
        String tradeNo = orderInfoVo.getTradeNo();
        String tradeNoKey = " tradeNo:" + userId + ":info";
        String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);
        redisScript.setScriptText(luaScript);
        Long count = (Long) redisTemplate.execute(redisScript, Arrays.asList(tradeNoKey), tradeNoKey);
        if (count == 0) {
            //  重复提交,抛出异常
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }
        //声明一个订单号
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "");
        //判断支付的类型
        if (!orderInfoVo.getItemType().equals(SystemConstant.ORDER_PAY_ACCOUNT)) {
            //在线支付 微信和支付宝
            this.saveOrder(orderNo, orderInfoVo, userId);
        } else {
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderNo);
            accountDeductVo.setUserId(userId);
            accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
            accountDeductVo.setContent("检查和扣减余额");
            Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
            if (200 != result.getCode()) {
                //  扣减失败，抛出异常
                throw new GuiguException(211, "新增购买记录异常");
            }
            this.saveOrder(orderNo, orderInfoVo, userId);
            //保存用户交易信息
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setItemType(orderInfoVo.getItemType());
            //获取id集合
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //远程更新用户表中的信息
            Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
            if (200 != userResult.getCode()) {
                throw new GuiguException(211, "新增购买记录异常");
            }

        }


        return orderNo;
    }


    /**
     * 取消订单
     * <p>
     * 本方法用于将一个有效订单的状态更改为已取消如果订单已经处于其他不可取消的状态，则不会进行任何操作
     *
     * @param orderNo 订单号，用于标识需要取消的订单
     */
    @Override
    public void cancelOrder(String orderNo) {
        // 获取订单信息
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        // 检查订单是否存在且状态为未支付
        if (orderInfo != null && orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)) {
            // 设置订单状态为已取消
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            // 更新订单信息
            this.orderInfoMapper.updateById(orderInfo);
        }
    }

    /**
     * 保存订单信息
     *
     * @param orderNo 订单号
     * @param orderInfoVo 订单信息VO
     * @param userId 用户ID
     */
    public void saveOrder(String orderNo, OrderInfoVo orderInfoVo, Long userId) {
        // 创建订单对象
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderNo(orderNo);
        orderInfo.setUserId(userId);
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfo.setOriginalAmount(orderInfoVo.getOriginalAmount());
        orderInfo.setOrderAmount(orderInfoVo.getOrderAmount());
        orderInfo.setDerateAmount(orderInfoVo.getDerateAmount());
        orderInfo.setPayWay(orderInfoVo.getPayWay());
        orderInfo.setItemType(orderInfoVo.getItemType());

        // 保存订单信息到数据库
        orderInfoMapper.insert(orderInfo);

        // 获取前端传入的订单明细
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        for (OrderDetailVo orderDetailVo : orderDetailVoList) {
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderInfo.getId());
            orderDetail.setItemId(orderDetailVo.getItemId());
            orderDetail.setItemName(orderDetailVo.getItemName());
            orderDetail.setItemUrl(orderDetailVo.getItemUrl());
            orderDetail.setItemPrice(orderDetailVo.getItemPrice());

            // 保存订单明细到数据库
            orderDetailMapper.insert(orderDetail);
        }

        // 获取前端传入的订单减免明细
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        for (OrderDerateVo orderDerateVo : orderDerateVoList) {
            OrderDerate orderDerate = new OrderDerate();
            orderDerate.setOrderId(orderInfo.getId());
            orderDerate.setDerateType(orderDerateVo.getDerateType());
            orderDerate.setDerateAmount(orderDerateVo.getDerateAmount());
            orderDerate.setRemarks(orderDerateVo.getRemarks());

            // 保存订单减免明细到数据库
            orderDerateMapper.insert(orderDerate);
        }

        // 判断支付方式
        if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)) {
            // 余额支付，修改订单状态
            this.orderPay(orderNo);
        } else {
            // 在线支付，发送延迟消息以取消订单
            this.rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderNo, MqConst.CANCEL_ORDER_DELAY_TIME);
        }
    }

    /**
     * 处理订单支付
     * 当订单未被取消时，将其状态更新为已支付
     *
     * @param orderNo 订单编号，用于识别和查询特定的订单
     */
    public void orderPay(String orderNo) {
        //在修改之前查询订单信息，看是否已经修改过
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        //检查订单是否存在且未被取消
        if (null != orderInfo && !orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_CANCEL)) {
            //设置订单状态为已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            //更新数据库中的订单信息
            this.updateById(orderInfo);
        }
    }

    /**
     * 根据订单号查询订单信息
     * <p>
     * 本方法通过订单号查询数据库，以获取订单的详细信息
     * 主要是为了处理与订单信息相关的业务逻辑，确保订单信息的准确性和完整性
     *
     * @param orderNo 订单号，唯一标识一个订单的字符串
     * @return 返回订单信息对象OrderInfo，包含订单的所有详情
     */
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        //创建查询条件封装类
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        //设置查询条件，根据订单号查询
        wrapper.eq(OrderInfo::getOrderNo, orderNo);
        //执行查询操作，获取订单信息
        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        //设置支付方式名称，根据支付方式代码转换为支付方式名称
        orderInfo.setPayWayName(orderInfo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT) ? "账户余额支付" : "在线支付");
        //返回订单信息
        return orderInfo;
    }

    /**
     * 订单交易
     *
     * @param tradeVo
     * @param userId
     * @return
     */

    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //初始化价格
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");
        //初始化明细列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //判断用户购买类型
        //购买专辑
        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {
            Result<Boolean> booleanResult = userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(booleanResult, "判断用户购买专辑失败");
            Boolean aBoolean = booleanResult.getData();
            //用户之前购买过专辑
            if (aBoolean) {
                throw new RuntimeException("重复购买");

            }
            //没购买过获取专辑信息
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull("albumInfoResult", "远程调用专辑服务失败");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull("albumInfo", "专辑不能为空");
            //付费前要判断用户的身份
            //获取用户信息
            Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
            Assert.notNull(userInfoVoResult, "获取用户信息失败");
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            Assert.notNull(userInfoVo, "主播信息不存在");
            //赋值
            originalAmount = albumInfo.getPrice();
            //vip有并且没过期
            if (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                if (albumInfo.getVipDiscount().compareTo(new BigDecimal("-1")) != -1) {
                    //vip折扣购买
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getVipDiscount()));
                    orderAmount = originalAmount.subtract(derateAmount);
                }


            } else {//普通用户购买专辑
                if (albumInfo.getVipDiscount().compareTo(new BigDecimal("-1")) != -1) {
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getDiscount()));
                    orderAmount = originalAmount.subtract(derateAmount);
                }

            }
            //支付明细
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVoList.add(orderDetailVo);
            if (derateAmount.compareTo(new BigDecimal("0.00")) == 1) {

                //折扣明细
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(tradeVo.getItemType());
                orderDerateVo.setRemarks("专辑购买");
                orderDerateVoList.add(orderDerateVo);
            } else if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)) {
                //  判断当前购买的声音数量应该大于等于0;
                if (tradeVo.getTrackCount() < 0) {
                    //  说明数据异常
                    throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
                }
                Result<List<TrackInfo>> paidTrackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
                Assert.notNull("paidTrackInfoListResult", "远程调用声音服务失败");
                List<TrackInfo> paidTrackInfoList = paidTrackInfoListResult.getData();
                Assert.notNull("paidTrackInfoList", "已购买声音不能为空");
                Result<AlbumInfo> albumInfoResult1 = albumInfoFeignClient.getAlbumInfo(paidTrackInfoList.get(0).getAlbumId());
                Assert.notNull(albumInfoResult1, "专辑信息不存在");
                AlbumInfo albumInfo1 = albumInfoResult1.getData();
                originalAmount = tradeVo.getTrackCount() == 0 ? albumInfo.getPrice() : albumInfo.getPrice().multiply(new BigDecimal(tradeVo.getTrackCount()));
                orderAmount = originalAmount.subtract(derateAmount);
                for (TrackInfo trackInfo : paidTrackInfoList) {
                    //赋值
                    OrderDetailVo orderDetailVo1 = new OrderDetailVo();
                    orderDetailVo1.setItemId(trackInfo.getId());
                    orderDetailVo1.setItemPrice(orderAmount);
                    orderDetailVo1.setItemName(trackInfo.getTrackTitle());
                    orderDetailVo1.setItemUrl(trackInfo.getCoverUrl());
                    orderDetailVoList.add(orderDetailVo);
                }

            }


        } else {//购买vip
            Result<VipServiceConfig> vipServiceConfigResult = vipServiceConfigFeignClient.getVipServiceConfig(userId);
            Assert.notNull("vipServiceConfigResult", "远程调用vip服务失败");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull("vipServiceConfig", "vip列表不能为空");
            orderAmount = vipServiceConfig.getDiscountPrice();
            originalAmount = vipServiceConfig.getPrice();
            derateAmount = originalAmount.subtract(orderAmount);

            //支付明细
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVoList.add(orderDetailVo);
            if (derateAmount.compareTo(new BigDecimal("0.00")) == 1) {
                //折扣明细
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(tradeVo.getItemType());
                orderDerateVo.setRemarks("vip购买");
                orderDerateVoList.add(orderDerateVo);
            }


        }


        //赋值
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        String tradeNoKey = " tradeNo:" + userId + ":info";
        this.redisTemplate.opsForValue().setIfAbsent(tradeNoKey, tradeNo);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        Map signMap = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        String sign = SignHelper.getSign(signMap);
        orderInfoVo.setSign(sign);
        return orderInfoVo;
    }
}
